package net.sourceforge.fidocadj;

import javax.swing.*;

import java.util.prefs.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.lang.reflect.*;

import net.sourceforge.fidocadj.circuit.*;
import net.sourceforge.fidocadj.circuit.controllers.*;
import net.sourceforge.fidocadj.circuit.model.*;
import net.sourceforge.fidocadj.export.*;
import net.sourceforge.fidocadj.geom.*;
import net.sourceforge.fidocadj.globals.*;
import net.sourceforge.fidocadj.layers.*;
import net.sourceforge.fidocadj.timer.*;
import net.sourceforge.fidocadj.graphic.*;

/** FidoMain.java
    SWING App: The starting point of FidoCadJ.

<pre>
    This file is part of FidoCadJ.

    FidoCadJ is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    FidoCadJ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FidoCadJ. If not,
    @see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>.

    Copyright 2008-2016 by Davide Bucci
</pre>

    @author Davide Bucci
*/

public class FidoMain
{
    private static CommandLineParser clp;

    /** The main method. Process the command line options and if necessary
        shows an instance of FidoFrame.
        @param args the command line arguments.
    */
    public static void main(String... args)
    {
        clp = new CommandLineParser();

        if (args.length>=1)
            clp.processArguments(args);

        applyOptimizationSettings(clp);

        // Now we proceed with all the operations: opening files, converting...
        if(clp.getHeadlessMode()) {
            // Creates a circuit object
            DrawingModel P = new DrawingModel();

            if("".equals(clp.getLoadFileName())) {
                System.err.println("You should specify a FidoCadJ file to"+
                    " read");
                System.exit(1);
            }

            // Reads the standard libraries
            readLibrariesProbeDirectory(P, false, clp.getLibDirectory());
            P.setLayers(StandardLayers.createStandardLayers());
            ParserActions pa=new ParserActions(P);

            MyTimer mt = new MyTimer();
            try {
                String txt = FileUtils.readFile(clp.getLoadFileName());
                // Here txt contains the new circuit: parse it!
                pa.parseString(new StringBuffer(txt));
            } catch(IllegalArgumentException iae) {
                System.err.println("Illegal filename");
            } catch(Exception e) {
                System.err.println("Unable to process: "+e);
            }

            if (clp.shouldConvertFile()) {
                doConvert(clp, P);
            }

            if (clp.getHasToPrintSize()) {
                PointG o=new PointG(0,0);
                DimensionG d = DrawingSize.getImageSize(P,1, true, o);
                System.out.println(""+d.width+" "+d.height);
            }

            if (clp.getHasToPrintTime()) {
                System.out.println("Elapsed time: "+mt.getElapsed()+" ms.");
            }
        }

        if (!clp.getCommandLineOnly()) {
            SwingUtilities.invokeLater(new CreateSwingInterface(
                clp.getLibDirectory(),
                clp.getLoadFileName(), clp.getWantedLocale()));
        }
    }

    /** Apply optimisation settings which are platform-dependent.
        @param clp command-line arguments may deactivate some optimisations.
    */
    private static void applyOptimizationSettings(CommandLineParser clp)
    {
        if(!clp.getStripOptimization() &&
            System.getProperty("os.name").startsWith("Mac"))
        {
            // CAREFUL**************************************************
            // In all MacOSX systems I tried, this greatly increases the
            // redrawing speed. *HOWEVER* the default value for Java 1.6
            // as distributed by Apple is "false" (whereas it was "true"
            // for Java 1.5).  This might mean that in a future this can
            // be not very useful, or worse slowdown the performances.
            // CAREFUL**************************************************
            // NOTE: this does not seem to have any effect!
            System.setProperty("apple.awt.graphics.UseQuartz", "true");
        }

        /* if(!clp.getStripOptimization() &&
            System.getProperty("os.name").toLowerCase().startsWith("linux")) {
            // CAREFUL**************************************************
            // Various sources  reports that  this option  will increase
            // the redrawing speed using Linux. It might happen, however
            // that the  performances  can be somewhat  degraded in some
            // systems.
            // CAREFUL**************************************************
            // We tested that in version 0.24.1. In fact, activating this
            // option renders the software inusable in some systems (Nvidia
            // graphic software?) So this option is definitively turned off.
            // System.setProperty("sun.java2d.opengl", "true");
            // See for example this discussion: http://tinyurl.com/axoxqcb
        }   */
    }

    /** Perform a conversion into a graphic file, from command line parameters.
        A file should have already been loaded and parsed into P.
        This routine also checks if the output file has a correct extension,
        coherent with the file format chosen.

        @param clp command-line arguments.
        @param P the model containing the drawing.
    */
    private static void doConvert(CommandLineParser clp, DrawingModel P)
    {
        if(!Globals.checkExtension(clp.getOutputFile(),
            clp.getExportFormat()) && !clp.getForceMode())
        {
            System.err.println(
                "File extension is not coherent with the "+
                "export output format! Use -f to skip this test.");
            System.exit(1);
        }

        try {
            if (clp.getResolutionBasedExport()) {
                ExportGraphic.export(new File(clp.getOutputFile()),  P,
                    clp.getExportFormat(), clp.getResolution(),
                    true,false,true, true);
            } else {
                ExportGraphic.exportSize(new File(clp.getOutputFile()),
                    P, clp.getExportFormat(), clp.getXSize(), clp.getYSize(),
                    true,false,true,true);
            }
            System.out.println("Export completed");
        } catch(IOException ioe) {
            System.err.println("Export error: "+ioe);
        }
    }

    /** Read all libraries, eventually by inspecting the directory specified
        by the user. There are three standard directories: IHRAM.FCL,
        FCDstdlib.fcl and PCB.fcl. If those files are found in the external
        directory specified, the internal version is not loaded. Other files
        on the external directory are loaded.

        @param P the parsing class in which the libraries should be loaded
        @param englishLibraries a flag to specify if the internal libraries
            should be loaded in English or in Italian.
        @param libDirectoryO the path of the external directory.
    */
    public static void readLibrariesProbeDirectory(DrawingModel P,
        boolean englishLibraries, String libDirectoryO)
    {
        String libDirectory=libDirectoryO;
        ParserActions pa = new ParserActions(P);

        synchronized(P) {
            if (libDirectory == null || libDirectory.length()<3)
                libDirectory = System.getProperty("user.home");

            readIHRAM(englishLibraries, libDirectory, pa);
            readFCDstdlib(englishLibraries, libDirectory, pa);
            readPCBlib(englishLibraries, libDirectory, pa);
            readEY_Libraries(englishLibraries, libDirectory, pa);
            readElecLib(englishLibraries, libDirectory, pa);
        }
    }

    /** Read the internal IHRAM library, unless a file called
        IHRAM.FCL is present in the library directory

        @param libDirectory path where to search for the library.
        @param englishLibraries specify if the English version of the lib
            should be loaded instead of the Italian one.
        @param pa the object by which the library will be crunched.
    */
    private static void readIHRAM(boolean englishLibraries,
        String libDirectory,
        ParserActions pa)
    {
        pa.loadLibraryDirectory(libDirectory);
        if (new File(Globals.createCompleteFileName(
            libDirectory,"IHRAM.FCL")).exists())
        {
            System.out.println("IHRAM library got from external file");
        } else {
            if(englishLibraries)
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/IHRAM_en.FCL"), "ihram");
            else
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/IHRAM.FCL"), "ihram");
        }
    }

    /** Read the internal FCDstdlib library, unless a file called
        FCDstdlib.fcl is present in the library directory

        @param libDirectory path where to search for the library.
        @param englishLibraries specify if the English version of the lib
            should be loaded instead of the Italian one.
        @param pa the object by which the library will be crunched.
    */
    private static void readFCDstdlib(boolean englishLibraries,
        String libDirectory,
        ParserActions pa)
    {
        if (new File(Globals.createCompleteFileName(
            libDirectory,"FCDstdlib.fcl")).exists())
        {
            System.out.println("Standard library got from external file");
        } else {
            if(englishLibraries)
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/FCDstdlib_en.fcl"), "");
            else
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/FCDstdlib.fcl"), "");
        }
    }

    /** Read the internal PCB library, unless a file called
        PCB.fcl is present in the library directory

        @param libDirectory path where to search for the library.
        @param englishLibraries specify if the English version of the lib
            should be loaded instead of the Italian one.
        @param pa the object by which the library will be crunched.
    */
    private static void readPCBlib(boolean englishLibraries,
        String libDirectory,
        ParserActions pa)
    {
        if (new File(Globals.createCompleteFileName(
            libDirectory,"PCB.fcl")).exists())
        {
            System.out.println("Standard PCB library got from external file");
        } else {
            if(englishLibraries)
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/PCB_en.fcl"), "pcb");
            else
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/PCB.fcl"), "pcb");
        }
    }

    /** Read the internal EY_Libraries library, unless a file called
        EY_Libraries.fcl is present in the library directory

        @param libDirectory path where to search for the library.
        @param englishLibraries specify if the English version of the lib
            should be loaded instead of the Italian one.
        @param pa the object by which the library will be crunched.
    */
    private static void readEY_Libraries(boolean englishLibraries,
        String libDirectory,
        ParserActions pa)
    {
        if (new File(Globals.createCompleteFileName(
            libDirectory,"EY_Libraries.fcl")).exists())
        {
            System.out.println("Standard EY_Libraries got from external file");
        } else {
            pa.loadLibraryInJar(FidoFrame.class.getResource(
                "lib/EY_Libraries.fcl"), "EY_Libraries");
        }
    }

    /** Read the internal elettrotecnica library, unless a file called
        elettrotecnica.fcl is present in the library directory

        @param libDirectory path where to search for the library.
        @param englishLibraries specify if the English version of the lib
            should be loaded instead of the Italian one.
        @param pa the object by which the library will be crunched.
    */
    private static void readElecLib(boolean englishLibraries,
        String libDirectory,
        ParserActions pa)
    {
        if (new File(Globals.createCompleteFileName(
            libDirectory,"elettrotecnica.fcl")).exists())
        {
            System.out.println(
                "Electrotechnics library got from external file");
        } else {
            if(englishLibraries)
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/elettrotecnica_en.fcl"), "elettrotecnica");
            else
                pa.loadLibraryInJar(FidoFrame.class.getResource(
                    "lib/elettrotecnica.fcl"), "elettrotecnica");
        }
    }
}

/** Creates the Swing elements needed for the interface.
*/
class CreateSwingInterface implements Runnable
{
    String libDirectory;
    String loadFile;
    Locale currentLocale;

    /** Constructor where we specify some details concerning the library
        directory, the file to load (if needed) as well as the locale.
        @param ld the library directory
        @param lf the file to load
        @param ll the locale.
    */
    public CreateSwingInterface (String ld, String lf, Locale ll)
    {
        libDirectory = ld;
        loadFile = lf;
        currentLocale =ll;
    }

    /** Standard constructor.
    */
    public CreateSwingInterface ()
    {
        libDirectory = "";
        loadFile = "";
    }

    /** Run the thread.
    */
    public void run()
    {
        /*******************************************************************
            PLATFORM SELECTION AND CONFIGURATION CODE GOES IN THIS SECTION
        *******************************************************************/
        if (System.getProperty("os.name").startsWith("Mac")) {
            Globals g=new Globals();

            Preferences prefs_static =
                Preferences.userNodeForPackage(g.getClass());

            Globals.quaquaActive = prefs_static.get("QUAQUA",
                "true").equals("true");

            Globals.weAreOnAMac =true;

            // These settings allows to obtain menus on the right place
            System.setProperty("com.apple.macos.useScreenMenuBar","true");
            // This is for JVM < 1.5 It won't harm on higher versions.
            System.setProperty("apple.laf.useScreenMenuBar","true");
            // This is for having the good application name in the menu
            System.setProperty(
                "com.apple.mrj.application.apple.menu.about.name",
                "FidoCadJ");

            try {
                //Globals.quaquaActive=true;
                //System.setProperty("Quaqua.Debug.showVisualBounds","true");
                //System.setProperty("Quaqua.Debug.showClipBounds","true");
                if(Globals.quaquaActive) {
                    UIManager.setLookAndFeel(
                        "ch.randelshofer.quaqua.QuaquaLookAndFeel");

                    System.out.println("Quaqua look and feel active");
                }

            } catch (Exception e) {
                // Quaqua is not active. Just continue!

                System.out.println(
                    "The Quaqua look and feel is not available");
                System.out.println(
                    "I will continue with the basic Apple l&f");
            }
        } else if (System.getProperty("os.name").startsWith("Win")) {
            /* If the host system is a window system, select the Windows
               look and feel. This is a way to encourage people to use
               FidoCadJ even on a Windows system, forgotting about Java.
            */
            try {
                UIManager.setLookAndFeel(
                    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            } catch (Exception E) {
                System.out.println("Could not load the Windows Look and feel!");
            }
            Globals.quaquaActive=false;
        } else {
            Globals.quaquaActive=false;
        }

        // Un-comment to try to use the Metal LAF

        /*
        try {
            UIManager.setLookAndFeel(
            UIManager.getCrossPlatformLookAndFeelClassName());
            Globals.weAreOnAMac =false;
        } catch (Exception E) {}
        */
        /*******************************************************************
                        END OF THE PLATFORM SELECTION CODE
        *******************************************************************/

        if(Globals.weAreOnAMac) {
            // Here we use the reflection provided by Java to understand
            // if the AppleSpecific class is available on the system.
            // This class should be compiled separately from the main
            // program since the compilation can be successful only on
            // a MacOSX system.

            try {
                Class<?> a = Class.forName(
                    "net.sourceforge.fidocadj.AppleSpecific");
                Object b = a.newInstance();
                Method m = a.getMethod("answerFinder");
                m.invoke(b);

            } catch (Exception exc) {
                Globals.weAreOnAMac = false;
                System.out.println("It seems that this software has been "+
                    "compiled on a system different from MacOSX. Some nice "+
                    "integrations with MacOSX will therefore be absent. If "+
                    "you have compiled on MacOSX, make sure you used the "+
                    "'compile' or 'rebuild' script along with the 'mac' "+
                    "option.");
            }
        }

        // Here we create the main window object

        FidoFrame popFrame=new FidoFrame(true, currentLocale);

        if (!"".equals(libDirectory)) {
            popFrame.libDirectory = libDirectory;
        }

        popFrame.init();

        // We begin by showing immediately the window. This improves the
        // perception of speed given to the user, since the libraries
        // are not yet loaded
        popFrame.setVisible(true);

        // We load the libraries (this does not take so long in modern
        // systems).
        popFrame.loadLibraries();
        // If a file should be loaded, load it now, since popFrame has been
        // created and initialized.
        if(!"".equals(loadFile))
            popFrame.getFileTools().load(loadFile);

        // We force a global validation of the window size, by including
        // this time the tree containing the various libraries and the
        // macros.
        popFrame.setVisible(true);
    }
}